using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.RecipeFramework;
using EnvDTE;
using System.Text.RegularExpressions;
using EnvDTE80;
using System.ComponentModel.Design;
using Microsoft.Practices.ComponentModel;
using Microsoft.Practices.Common;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using VSSDKAssist.Assets.Actions.Base;
using Microsoft.Practices.RecipeFramework.Library;

namespace VSSDKAssist.Assets.Actions.VisualStudio
{
	/// <summary>
	/// Action that insert text on a text file
	/// </summary>
	public class InsertTextAction : ActionBase
	{
		#region Properties
		private ProjectItem projectItem;

		/// <summary>
		/// Gets or sets the project item.
		/// </summary>
		/// <value>The project item.</value>
		[Input(Required = true)]
		public ProjectItem ProjectItem
		{
			get
			{
				return this.projectItem;
			}
			set
			{
				this.projectItem = value;
			}
		}

		private string textToInsert;

		/// <summary>
		/// Gets or sets the text to insert.
		/// </summary>
		/// <value>The text to insert.</value>
		[Input(Required = true)]
		public string TextToInsert
		{
			get
			{
				return this.textToInsert;
			}
			set
			{
				this.textToInsert = value;
			}
		}

		private string searchExpression;

		/// <summary>
		/// Gets or sets the search expression.
		/// </summary>
		/// <value>The search expression.</value>
		[Input(Required = true)]
		public string SearchExpression
		{
			get
			{
				return this.searchExpression;
			}
			set
			{
				this.searchExpression = value;
			}
		}

		private InsertPosition position = InsertPosition.After;

		/// <summary>
		/// Gets or sets the position.
		/// </summary>
		/// <value>The position.</value>
		[Input(Required = true)]
		public InsertPosition InsertPosition
		{
			get
			{
				return this.position;
			}
			set
			{
				this.position = value;
			}
		}
		#endregion

		#region Protected Implementation
		/// <summary>
		/// Called when [execute].
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		protected override void OnExecute()
		{
			try
			{
				projectItem.Open(Constants.vsViewKindTextView);

				TextDocument document = projectItem.Document.Object("TextDocument") as TextDocument;
				EditPoint editPoint = document.CreateEditPoint(null);

				EditPoint endPoint = null;
				TextRanges tags = null;
				string expression = EvaluateLiteral(Regex.Escape(Regex.Unescape(this.searchExpression))).ToString();
				bool patternFound = editPoint.FindPattern(
					expression,
					(int)(vsFindOptions.vsFindOptionsRegularExpression | vsFindOptions.vsFindOptionsFromStart),
					ref endPoint, ref tags);

				if(!patternFound)
				{
					return;
				}

				//Find the last match
				while(patternFound)
				{
					editPoint.MoveToAbsoluteOffset(endPoint.AbsoluteCharOffset);
					endPoint = null;
					tags = null;
					patternFound = editPoint.FindPattern(expression, (int)(vsFindOptions.vsFindOptionsRegularExpression),
					ref endPoint, ref tags);
				}

				InsertLine(editPoint, EvaluateLiteral(this.textToInsert).ToString(), editPoint.Line, this.position);

				projectItem.Document.Close(vsSaveChanges.vsSaveChangesYes);
			}
			catch
			{
				if(projectItem.Document != null)
				{
					projectItem.Document.Close(vsSaveChanges.vsSaveChangesNo);
				}
			}
		}

		/// <summary>
		/// Called when [undo].
		/// </summary>
		protected override void OnUndo()
		{
			//Do nothing
		}
		#endregion

		#region Private Implementation
		private object EvaluateLiteral(string expression)
		{
			IDictionaryService dictionaryService = (IDictionaryService)this.GetService(typeof(IDictionaryService));

			return new ExpressionEvaluationService().Evaluate(expression, new ServiceAdapterDictionary(dictionaryService));
		}

		private static void InsertLine(EditPoint editPoint, string text, int line, InsertPosition pos)
		{
			editPoint.MoveToLineAndOffset(line, 1);

			if(pos == InsertPosition.After)
			{
				editPoint.LineDown(1);
			}

			((EditPoint2)editPoint).InsertNewLine(1);
			editPoint.LineUp(1);

			editPoint.Insert(text);
		}
		#endregion
	}
}